
#ifndef DIRENT_INCLUDED
#define DIRENT_INCLUDED

/*

    Declaration of POSIX directory browsing functions and types for Win32.

    Author:  Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
    History: Created March 1997. Updated June 2003.
    Rights:  See end of file.

*/

#include <errno.h>
#include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
	#pragma warning(disable : 4996)
#endif


#ifdef __cplusplus
	extern "C"
	{
#endif

#ifndef _INO_T_
	typedef unsigned short _ino_t;          /* i-node number (not used on DOS) */
	#define _INO_T_
#endif

/* The type is unknown. On some systems this is the only value returned. */
#define DT_UNKNOWN	1

/* A regular file. */
#define DT_REG			2

/* A directory. */
#define DT_DIR			4

/* A named pipe, or FIFO. */
#define DT_FIFO			8

/* A local-domain socket. */
#define DT_SOCK			16

/* A character device. */
#define DT_CHR			32

/* A block device. */
#define DT_BLK			64

typedef struct DIR DIR;

struct dirent
{
	_ino_t					d_ino;
	//off_t					  d_off;
	unsigned short	d_reclen;
	char*						d_name; /* We must not include limits.h! */
	unsigned char		d_type;	/* This is the type of the file, possibly unknown. */
};

#ifndef __STRICT_ANSI__
	struct dirent64
	{
		_ino_t					d_ino;
		//int64_t				  d_off;
		unsigned short	d_reclen;
		char*						d_name;
		unsigned char		d_type;	/* This is the type of the file, possibly unknown. */
	};
#endif

struct DIR
{
    long                handle; /* -1 for failed rewind */
    struct dirent       result; /* d_name null iff first time */
    char*               name;  /* null-terminated char string */
    char*		folder;  /* null-terminated char string */
};


inline DIR*		opendir(const char *name)
{
    DIR *dir = 0;

    if(name && name[0])
    {
			size_t base_length = strlen(name);
			const char* all = NULL;
			const char* slash = NULL;

			/* search pattern must end with suitable wildcard */
			slash = strchr("/\\", name[base_length - 1]) ? "" : "/";
			all = strchr("/\\", name[base_length - 1]) ? "*" : "/*";

      if( (dir = (DIR *) malloc(sizeof *dir)) != 0 &&
          (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0 && 
					(dir->folder = (char *) malloc(base_length + strlen(slash) + 1)) != 0 )
			{
				strcat(strcpy(dir->folder, name), slash);
        strcat(strcpy(dir->name, name), all);

				struct _finddata_t  info;

        if((dir->handle = (long) _findfirst(dir->name, &info)) != -1)
        {
					dir->result.d_name = 0;
        } else /* rollback */
        {
					free(dir->name);
          free(dir);
          dir = 0;
        }
      } else /* rollback */
      {
				free(dir);
				dir   = 0;
				errno = ENOMEM;
      }
    } else
    {
			errno = EINVAL;
    }

    return dir;
}

inline int closedir(DIR *dir)
{
  int result = -1;

  if(dir)
  {
		if(dir->handle != -1)
		{
			result = _findclose(dir->handle);
		}

		free(dir->name);
		free(dir);
  }

  if(result == -1) /* map all errors to EBADF */
  {
      errno = EBADF;
  }

  return result;
}

inline struct dirent*	readdir(DIR *dir)
{
  struct dirent *result = 0;

  if(dir && dir->handle != -1)
  {
		struct _finddata_t  info;

    if(!dir->result.d_name || _findnext(dir->handle, &info) != -1)
    {
			struct _stat filestat;
			char* fullfilepath = NULL;
			size_t base_length = strlen(dir->folder);

      result         = &dir->result;
      result->d_name = info.name;
			result->d_reclen = (unsigned short)strlen(result->d_name);
			result->d_ino = (_ino_t)-1;
		
			if( (fullfilepath = (char *) malloc(base_length + result->d_reclen + 1)) != 0)
			{
				strcat(strcpy(fullfilepath, dir->folder), info.name);
				if(_stat(fullfilepath, &filestat ) == 0)
				{
					result->d_ino = filestat.st_ino;
				}
				free(fullfilepath);
			}
			result->d_type = DT_UNKNOWN;
			if(info.attrib & _A_SUBDIR)
			{
				result->d_type = DT_DIR;
			} else
			{
				result->d_type = DT_REG;
			}
    }
  } else
  {
		errno = EBADF;
  }

  return result;
}


inline void		rewinddir(DIR *dir)
{
  if(dir && dir->handle != -1)
  {
		struct _finddata_t  info;

    _findclose(dir->handle);
    dir->handle = (long) _findfirst(dir->name, &info);
    dir->result.d_name = 0;
  } else
  {
    errno = EBADF;
  }
}

/*

    Copyright Kevlin Henney, 1997, 2003. All rights reserved.

    Permission to use, copy, modify, and distribute this software and its
    documentation for any purpose is hereby granted without fee, provided
    that this copyright and permissions notice appear in all copies and
    derivatives.

    This software is supplied "as is" without express or implied warranty.

    But that said, if there are any problems please get in touch.

*/

#ifdef __cplusplus
}
#endif

#endif


/*

    Implementation of POSIX directory browsing functions and types for Win32.

    Author:  Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
    History: Created March 1997. Updated June 2003.
    Rights:  See end of file.

*/
